// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 整理一下

package modcmd

import (
	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/imports"
	"cmd/go/internal/modload"
	"context"
	"fmt"

	"golang.org/x/mod/modfile"
	"golang.org/x/mod/semver"
)

var cmdTidy = &base.Command{
	UsageLine: "go mod tidy [-e] [-v] [-go=version] [-compat=version]",
	Short:     "add missing and remove unused modules",
	Long: `
Tidy makes sure go.mod matches the source code in the module.
It adds any missing modules necessary to build the current module's
packages and dependencies, and it removes unused modules that
don't provide any relevant packages. It also adds any missing entries
to go.sum and removes any unnecessary ones.

The -v flag causes tidy to print information about removed modules
to standard error.

The -e flag causes tidy to attempt to proceed despite errors
encountered while loading packages.

The -go flag causes tidy to update the 'go' directive in the go.mod
file to the given version, which may change which module dependencies
are retained as explicit requirements in the go.mod file.
(Go versions 1.17 and higher retain more requirements in order to
support lazy module loading.)

The -compat flag preserves any additional checksums needed for the
'go' command from the indicated major Go release to successfully load
the module graph, and causes tidy to error out if that version of the
'go' command would load any imported package from a different module
version. By default, tidy acts as if the -compat flag were set to the
version prior to the one indicated by the 'go' directive in the go.mod
file.

See https:// golang.org/ref/mod#go mod tidy了解更多关于go mod tidy的信息。
	`,
	Run: runTidy,
}

var (
	tidyE      bool          // 如果为true，则报告错误，但仍要继续。
	tidyGo     goVersionFlag // go版本写入整理过的go.mod文件（切换延迟加载）
	tidyCompat goVersionFlag // 整理过的go.mod和go.sum文件应“兼容”的go版本
)

func init() {
	cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
	cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
	cmdTidy.Flag.Var(&tidyGo, "go", "")
	cmdTidy.Flag.Var(&tidyCompat, "compat", "")
	base.AddModCommonFlags(&cmdTidy.Flag)
}

// goVersionFlag是一个标志。值表示支持的Go版本。
// None
// （请注意，'go mod edit'的-go参数是*而不是*一个goVersionFlag。
// 它有意允许将比支持的版本更新的版本作为参数。）
type goVersionFlag struct {
	v string
}

func (f *goVersionFlag) String() string   { return f.v }
func (f *goVersionFlag) Get() interface{} { return f.v }

func (f *goVersionFlag) Set(s string) error {
	if s != "" {
		latest := modload.LatestGoVersion()
		if !modfile.GoVersionRE.MatchString(s) {
			return fmt.Errorf("expecting a Go version like %q", latest)
		}
		if semver.Compare("v"+s, "v"+latest) > 0 {
			return fmt.Errorf("maximum supported Go version is %s", latest)
		}
	}

	f.v = s
	return nil
}

func runTidy(ctx context.Context, cmd *base.Command, args []string) {
	if len(args) > 0 {
		base.Fatalf("go mod tidy: no arguments allowed")
	}

	// Tidy的目标是使“go测试”对“all”中的任何包装都具有可复制性，因此我们
	// 需要包括测试依赖项。对于指定go 1.15或
	// 早些时候，这是一个不可操作的测试（因为“全部”饱和了传递性测试）
	// 依赖项）。
	// None
	// 但是，对于延迟加载（go 1.16+）“all”仅包括
	// 由主模块传递导入，而不是的测试依赖项
	// 那些包裹。为了使包装的“go测试”重现性
	// 除了主模块之外的所有模块，我们必须明确
	// 请求包括他们的测试依赖项。
	modload.ForceUseModules = true
	modload.RootMode = modload.NeedRoot

	modload.LoadPackages(ctx, modload.PackageOpts{
		GoVersion:                tidyGo.String(),
		Tags:                     imports.AnyTags(),
		Tidy:                     true,
		TidyCompatibleVersion:    tidyCompat.String(),
		VendorModulesInGOROOTSrc: true,
		ResolveMissingImports:    true,
		LoadTests:                true,
		AllowErrors:              tidyE,
		SilenceMissingStdImports: true,
	}, "all")
}
